import sys
import math
from typing import List
from PySide6.QtWidgets import (QApplication, QMainWindow, QGraphicsView, QGraphicsScene, 
                              QGraphicsItem, QGraphicsPixmapItem, QGraphicsRectItem, 
                              QGraphicsPolygonItem, QGraphicsLineItem, QGraphicsEllipseItem,
                              QGraphicsItemGroup, QGraphicsTextItem, QDockWidget, 
                              QToolBar, QMenu, QFileDialog, QColorDialog, 
                              QSlider, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QGridLayout,
                              QComboBox, QToolButton, QSpinBox, QDoubleSpinBox,
                              QSplitter, QMessageBox, QPushButton,QDialog,QWidgetAction,QScrollArea,
                              QGraphicsSceneMouseEvent,QAbstractGraphicsShapeItem,QLineEdit,QButtonGroup,QRadioButton
                              )
from PySide6.QtCore import Qt, QPointF, QPoint, QRectF,  QRect, QLineF, QSizeF, QUrl, QMimeData, Signal, QObject,QThread,QMarginsF,QEvent, QTimer
from PySide6.QtGui import (QPixmap, QImage, QImageReader,QPainter, QPen, QBrush, QColor, QPolygonF, QPolygon,QShortcut,
                          QTransform, QCursor, QFont, QIcon, QKeySequence, QDrag, QUndoStack, QFocusEvent,
                          QUndoCommand, QAction,QActionGroup,QMouseEvent,QResizeEvent
                          )
import cv2
import numpy as np
import datetime
import traceback
import requests
import time

from gui.window.annotator.graphics_items import *



class PropertyDialog(QDialog):
    """属性设置对话框，支持动态更新属性、网格布局展示多标签及滚动查看"""
    
    def __init__(self, shape_properties=None, parent=None):
        super().__init__(parent)
        self.shape_properties = shape_properties or {}  # 标签与颜色的映射
        self.selected_label = None  # 选中的标签
        self.color = ""
        self.transliterate_text = ""  # 转写内容
        self.radio_buttons = []  # 存储单选按钮实例
        self.shortcuts = []  # 存储快捷键实例
        self.is_input_focused = False  # 输入框是否获得焦点
        self.attributes_color = {}
        self.init_ui()

    def init_ui(self):
        """初始化对话框UI"""
        self.setWindowTitle("属性选择")
        # 设置无边框样式（可选，根据需求决定是否完全隐藏标题栏）
        # self.setWindowFlags(Qt.FramelessWindowHint)
        
        self.setMinimumWidth(400)
        self.setMaximumHeight(500)  # 限制最大高度，配合滚动
        self.setModal(True)
        
        # 安装事件过滤器监控焦点变化
        self.installEventFilter(self)
        # 主布局
        main_layout = QVBoxLayout(self)
        main_layout.setContentsMargins(15, 15, 15, 15)
        main_layout.setSpacing(15)
        
        # 1. 转写内容输入区域
        transliterate_layout = QVBoxLayout()
        transliterate_label = QLabel("转写内容:")
        transliterate_label.setFont(QFont("SimHei", 9))
        
        self.transliterate_edit = QLineEdit()
        self.transliterate_edit.setPlaceholderText("请输入转写内容")
        self.transliterate_edit.setFont(QFont("SimHei", 10))
        # 设置输入框样式
        self.transliterate_edit.setStyleSheet("""
            padding: 5px;
            border: 1px solid #ccc;
            border-radius: 3px;
        """)
        # 监控输入框焦点变化
        self.transliterate_edit.focusInEvent = self.on_input_focus_in
        self.transliterate_edit.focusOutEvent = self.on_input_focus_out
        
        transliterate_layout.addWidget(transliterate_label)
        transliterate_layout.addWidget(self.transliterate_edit)
        
        # 2. 标签选择区域（带滚动功能）
        label_container = QWidget()
        self.label_layout = QVBoxLayout(label_container)  # 外层布局
        self.label_layout.setContentsMargins(0, 0, 0, 0)
        
        # 标签标题
        label_group = QLabel("选择标签:")
        label_group.setFont(QFont("SimHei", 9, QFont.Bold))
        self.label_layout.addWidget(label_group)
        
        # 滚动区域
        scroll_area = QScrollArea()
        scroll_area.setWidgetResizable(True)  # 自适应内容大小
        scroll_area.setStyleSheet("""
            QScrollArea {
                border: none;   /* 去掉滚动区域边框 */
            }
            QScrollBar:vertical {
                width: 8px;
                margin: 5px 0;
            }
            QScrollBar::handle:vertical {
                background: #ccc;
                border-radius: 4px;
            }
        """)
        
        # 网格布局容器（用于存放标签按钮）
        self.grid_container = QWidget()
        self.grid_layout = QGridLayout(self.grid_container)
        self.grid_layout.setContentsMargins(0, 0, 0, 0)
        self.grid_layout.setSpacing(8)  # 网格间距
        self.grid_layout.setColumnStretch(0, 1)
        self.grid_layout.setColumnStretch(1, 1)  # 两列等宽
        
        # 按钮组（管理单选按钮）
        self.radio_group = QButtonGroup(self)
        self.radio_group.setExclusive(True)  # 排他模式（只能选一个）
        
        # 初始化标签按钮
        self._update_label_buttons()
        
        # 组装滚动区域
        scroll_area.setWidget(self.grid_container)
        self.label_layout.addWidget(scroll_area)
        
        # 3. 按钮区域
        btn_layout = QHBoxLayout()
        btn_layout.setSpacing(10)
        
        confirm_btn = QPushButton("确认")
        confirm_btn.setFont(QFont("SimHei", 9))
        confirm_btn.setMinimumWidth(80)
        confirm_btn.setStyleSheet("""
            background-color: #4a86e8;
            color: white;
            border: none;
            border-radius: 3px;
            padding: 5px 0;
        """)
        confirm_btn.clicked.connect(self.accept)
        
        cancel_btn = QPushButton("取消")
        cancel_btn.setFont(QFont("SimHei", 9))
        cancel_btn.setMinimumWidth(80)
        cancel_btn.setStyleSheet("""
            background-color: #f0f0f0;
            border: 1px solid #ccc;
            border-radius: 3px;
            padding: 5px 0;
        """)
        cancel_btn.clicked.connect(self.reject)
        
        btn_layout.addStretch()
        btn_layout.addWidget(confirm_btn)
        btn_layout.addWidget(cancel_btn)
        
        # 组装主布局
        main_layout.addLayout(transliterate_layout)
        main_layout.addWidget(label_container)
        main_layout.addLayout(btn_layout)
        # 初始焦点设置到第一个单选按钮
        self.set_initial_focus()

    def set_initial_focus(self):
        """设置初始焦点到第一个单选按钮"""
        if self.radio_buttons:
            self.radio_buttons[0].setFocus(Qt.MouseFocusReason)
        else:
            self.transliterate_edit.setFocus(Qt.MouseFocusReason)
        self.setTabOrder(self.grid_container, self.transliterate_edit)

    def on_input_focus_in(self, event: QFocusEvent):
        """输入框获得焦点时的处理"""
        self.is_input_focused = True
        # 调用原始的focusInEvent确保正常行为
        QLineEdit.focusInEvent(self.transliterate_edit, event)

    def on_input_focus_out(self, event: QFocusEvent):
        """输入框失去焦点时的处理"""
        self.is_input_focused = False
        # 调用原始的focusOutEvent确保正常行为
        QLineEdit.focusOutEvent(self.transliterate_edit, event)

    def eventFilter(self, obj, event: QEvent):
        """事件过滤器，处理Tab键焦点切换"""
        # 处理Tab键切换焦点
        if event.type() == QEvent.KeyRelease and event.key() == Qt.Key_Tab:
            if not self.is_input_focused:
                self.transliterate_edit.clearFocus()  # 取消输入框焦点
                self.grid_container.setFocus(Qt.TabFocusReason)
                if self.radio_buttons:
                    self.radio_buttons[0].setFocus(Qt.MouseFocusReason)
            return
        return super().eventFilter(obj, event)

    def _update_label_buttons(self):
        """更新标签按钮（清除旧按钮，添加新按钮）"""
        # 清除原有按钮
        for btn in self.radio_buttons:
            self.radio_group.removeButton(btn)
            self.grid_layout.removeWidget(btn)
            btn.deleteLater()  # 彻底删除控件
        self.radio_buttons.clear()
        for shortcut in self.shortcuts:
            shortcut.deleteLater()
        self.shortcuts.clear()
        self.attributes_color.clear()
        # 添加新按钮（网格布局，2列）
        if self.shape_properties.get("annotation_method",None) == "分类":
            for idx, category in enumerate(self.shape_properties.get("categories",[])):
                label = category["name"]
                color = category["color"]
                shortcut_str = category.get("shortcut", "")  # 从配置获取快捷键
                radio_btn = QRadioButton(label)
                radio_btn.setFont(QFont("SimHei", 10))
                # 设置选中状态的颜色指示器
                radio_btn.setStyleSheet(f"""
                    QRadioButton::indicator {{
                        width: 14px;
                        height: 14px;
                        border: 1px solid #999;
                        border-radius: 7px;
                    }}
                    QRadioButton::indicator:checked {{
                        background-color: {color};
                        border-color: {color};
                    }}
                """)
                radio_btn.color = color
                # 计算网格位置（2列布局）
                row = idx // 2
                col = idx % 2
                self.grid_layout.addWidget(radio_btn, row, col)
                
                self.radio_buttons.append(radio_btn)
                self.radio_group.addButton(radio_btn)
                # 绑定快捷键（支持组合键如Ctrl+A、Shift+2等）
                if shortcut_str:
                    try:
                        # 创建快捷键，上下文为当前对话框
                        shortcut = QShortcut(QKeySequence(shortcut_str), self)
                        # 快捷键触发时选中对应按钮
                        shortcut.activated.connect(radio_btn.click)
                        self.shortcuts.append(shortcut)
                    except Exception as e:
                        print(e)
                        # 处理无效快捷键格式
                        radio_btn.setText(f"{label} (无效快捷键)")
            self.set_initial_focus()
        else: # 文本转写
            pass
        
        # 默认选中第一个标签
        if self.radio_buttons:
            self.radio_buttons[0].setChecked(True)

    def set_attributes(self, attributes):
        """更新属性并刷新UI"""
        if not isinstance(attributes, dict):
            return
        self.shape_properties = attributes
        self._update_label_buttons()  # 重新创建标签按钮

    def accept(self):
        """确认按钮处理：保存用户输入"""
        # 获取选中的标签
        checked_btn = self.radio_group.checkedButton()
        if checked_btn:
            self.selected_label = checked_btn.text()
            self.color = checked_btn.color
        
        # 获取转写内容
        self.transliterate_text = self.transliterate_edit.text()
        
        super().accept()

    def get_results(self):
        """获取用户输入结果"""
        return {
            "label": self.selected_label,
            "transliterate": self.transliterate_text,
            "color": self.color
        }
            

class ImageView(QGraphicsView):
    update_brightness_Signal = Signal(QPixmap)
    updateAnnotationsChanged = Signal()
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.undo_stack = QUndoStack(self)
        
        self.setScene(QGraphicsScene(self))
        self.setRenderHint(QPainter.Antialiasing)
        self.setRenderHint(QPainter.SmoothPixmapTransform)
        self.setDragMode(QGraphicsView.RubberBandDrag)
        self.setBackgroundBrush(QBrush(QColor(240, 240, 240)))
        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        self.setResizeAnchor(QGraphicsView.AnchorViewCenter)
        self.setAlignment(Qt.AlignCenter)
        self.setAcceptDrops(True)  # 设置可支持拖拽功能
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        
        # 缩放相关属性
        self.scale_factor = 1.1
        self.min_scale = 0.1  # 最小缩放比例
        self.max_scale = 10.0  # 最大缩放比例
        
        # 保持边框和控制点大小的相关属性
        self.base_border_width = 3.0  # 基础边框宽度
        self.base_handle_size = 8.0  # 基础控制点大小
        self.base_text_size = 20.0  # 基础控制点大小
        
        self.copy_items = []
        self.copy_action = QAction("复制", self)
        self.copy_action.setShortcut(QKeySequence.StandardKey.Copy)
        self.copy_action.triggered.connect(self.copy_item)
        self.paste_action = QAction("粘贴", self)
        self.paste_action.setShortcut(QKeySequence.StandardKey.Paste)
        self.paste_action.triggered.connect(self.paste_item)
        self.addAction(self.copy_action)
        self.addAction(self.paste_action)
        
        self.image_item = QGraphicsPixmapItem()
        self.scene().addItem(self.image_item)
        self.pan = False
        self.brightness = 0
        self.current_tool = 'rectangle'
        self.draw_temp_items:dict[str, QGraphicsItem] = {
            'rectangle': ResizableGraphicsRectItem(0, 0, 0, 0),
            'quadrilateral': ResizableGraphicsQuadrilateralItem(QPolygonF()),
            'polygon': ResizableGraphicsPolygonItem(QPolygonF()),
            'arrow': ResizableArrowItem(QLineF(QPointF(), QPointF()))
        }
        self.draw_temp_items_pos = {
            'rectangle': [QPointF(), QPointF()],
            'quadrilateral': [],
            'polygon': [],
            'arrow': [QPointF(), QPointF()],
        }
        current_scale = self.get_current_scale()
        for key in self.draw_temp_items:
            self.draw_temp_items[key].setFlag(QGraphicsItem.ItemIsMovable, False)
            self.draw_temp_items[key].setFlag(QGraphicsItem.ItemIsSelectable, False)
            self.draw_temp_items[key].setVisible(False)
            self.draw_temp_items[key].setPen(QPen(Qt.GlobalColor.yellow, 2))
            self.draw_temp_items[key].set_pen_size(current_scale)
            self.draw_temp_items[key].setParentItem(self.image_item)
            if self.draw_temp_items[key] not in self.scene().items():  # 删除失败
                self.scene().addItem(self.draw_temp_items[key])
            
        
        self.is_draw_item = False  # 明确初始状态为False
        self.is_click = False  # 属于点击还是拖动
        self._last_left_click_time_pos = [0,QPointF(),True]  # 记录上一次点击的坐标和时间
        self.click_time_threshold = 0.3 # 点击时间阈值，小于这个时间间隔则认为是点击，大于就是选择拖拽
        self.timer_is_click = QTimer()
        self.timer_is_click.setInterval(50)  # 间隔 100ms
        self.timer_is_click.timeout.connect(self.to_time_is_click)
        self.timer_is_click.start()
        
        self.original_pixmap = None
        self.current_pixmap = None
        
        self.shape_properties = {}
        self.current_property = '车辆'
        self.set_brightness_thred = QThread()
        self.set_brightness_thred.parent = self
        self.set_brightness_thred.run = self.update_pixmap_brightness
        # self.set_brightness.connect(lambda x: self.view.set_brightness.set_brightness(x))
        self.update_brightness_Signal.connect(lambda x: self.image_item.setPixmap(self.current_pixmap))
        self.setViewportUpdateMode(QGraphicsView.MinimalViewportUpdate)
        
        self.image_item_transform = False
        self.property_dialog = PropertyDialog(self.shape_properties, self)

    def to_time_is_click(self):
        """根据时间间隔判断是否点击"""
        if time.time() - self._last_left_click_time_pos[0] < self.click_time_threshold or not self._last_left_click_time_pos[2]:  # 判断点击时间间隔
            self.is_click = True
            # self.timer_is_click.stop()
        else:
            self.is_click = False
            
    @staticmethod
    def get_rect_from_quad(quad_points:QPolygon):
        """
        根据四边形四个顶点计算最优拟合矩形
        返回: 目标矩形尺寸和变换后的四个顶点坐标
        """
        # 计算四边形各边长度
        def distance(p1, p2):
            p1 = p1.toTuple()
            p2 = p2.toTuple()
            return np.sqrt((p2[0] - p1[0])**2 + (p2[1] - p1[1])**2)
        
        # 四边形顶点
        p0, p1, p2, p3 = quad_points.toList()
        
        # 计算四边形各边长度
        top_width = distance(p0, p1)
        bottom_width = distance(p3, p2)
        left_height = distance(p0, p3)
        right_height = distance(p1, p2)
        
        # 目标矩形尺寸（取平均值）
        width = int((top_width + bottom_width) / 2)
        height = int((left_height + right_height) / 2)
        
        # 目标矩形的四个顶点
        target_points = [
            (0, 0),                   # 左上
            (width - 1, 0),           # 右上
            (width - 1, height - 1),  # 右下
            (0, height - 1)           # 左下
        ]
        target_points = QPolygonF([QPointF(x, y) for x, y in target_points])
        
        return (width, height), target_points
    
    def affine_transform_quadrilateral(self):
        selectedItems:List[QGraphicsItem] = self.scene().selectedItems()
        # 计算仿射变换矩阵
        transform = QTransform()
        if not self.image_item_transform and selectedItems:
            quad_points:QPolygon = selectedItems[0].polygon()
            # 定义目标矩形的四个顶点
            (width, height), dst_points = self.get_rect_from_quad(quad_points)
            a = transform.quadToQuad(quad_points, dst_points,transform)
            self.image_item_transform = True
        else:
            self.image_item_transform = False

        # 应用仿射变换到四边形
        self.image_item.setTransform(transform)
        # 应用相同的仿射变换到其他子项
        # self.other_item.setTransform(transform)
            
    
    def set_brightness(self, value):
        self.brightness = value
        if self.original_pixmap:
            self.set_brightness_thred.start()
            # self.update_pixmap_brightness()
            
    def update_pixmap_brightness(self):
        if not self.original_pixmap:
            return
            
        image = self.original_pixmap.toImage()
        # 将QImage转换为numpy数组
        # buffer = image.bits().tostring(image.byteCount())
        # img_arr = np.frombuffer(buffer, dtype=np.uint8).reshape((image.height(), image.width(), 4))
        
        # # 增加亮度，这里的value可以根据需要调整
        # value = 50  # 增加的亮度值
        # image_np = np.clip(img_arr + self.brightness, 0, 255).astype(np.uint8)
        # # 将numpy数组转换回QImage
        # height, width, channel = image_np.shape
        # image = QImage(image_np.data, width, height, width * channel, QImage.Format.Format_RGB888)
        # width = image.width()
        # height = image.height()
        # for y in range(height):
        #     for x in range(width):
        #         pixel = image.pixel(x, y)
        #         color = QColor(pixel)
                
        #         h, s, v, a = color.getHsvF()
        #         v = max(0.0, min(1.0, v + self.brightness / 100.0))
                
        #         new_color = QColor.fromHsvF(h, s, v, a)
        #         image.setPixelColor(x, y, new_color)
                
        self.current_pixmap = QPixmap.fromImage(image)
        self.update_brightness_Signal.emit(self.current_pixmap)
        
        
    def set_shape_properties(self, attributes:dict):
        self.shape_properties = attributes
        self.property_dialog.set_attributes(attributes)
        
    def get_shape_attribute_color(self,name):
        pass
        if self.shape_properties.get("annotation_method",None) == "分类":
            for idx, category in enumerate(self.shape_properties.get("categories",[])):
                label = category["name"]
                color = category["color"]
                if name == label:
                    return color
        return ""
        
        
    def set_image(self, pixmap):
        if isinstance(pixmap, str):
            if pixmap.startswith("http"):
                file_url = pixmap[:]
                pixmap = QPixmap()
                pixmap.loadFromData(requests.get(file_url).content)
            else:
                # img = QImage(pixmap)
                reader = QImageReader(pixmap)
                reader.setAutoTransform(False)  # 禁用自动转换以避免eXIf处理警告
                image = reader.read()
                if image.isNull():
                    raise ValueError(f"无法加载{pixmap} 图片: {reader.errorString()}")
                pixmap = QPixmap.fromImage(image)
                pixmap = QPixmap(pixmap)
        self.original_pixmap = pixmap
        self.current_pixmap = pixmap.copy()
        self.image_item.setPixmap(self.current_pixmap)
        # self.image_item.setFlag(QGraphicsItem.ItemIsMovable, False)
        # self.image_item.setFlag(QGraphicsItem.ItemIsSelectable, False)
        self.image_item.setZValue(1)
        self.clase_draw()
        self.fit_and_center()
        for item in self.get_image_child_items(): # 这里是正常的清空不计历史入栈
            if item not in self.draw_temp_items.values():
                self.scene().removeItem(item) 
        # self.setSceneRect(self.image_item.boundingRect())
        
    def get_image_child_items(self) -> list:
        """获取图像元素的所有子项（排除临时绘制元素）"""
        temp_items = set(self.draw_temp_items.values())
        return [item for item in self.image_item.childItems() if item not in temp_items]
    
    def clase_draw(self,) -> None:
        for tool,item in self.draw_temp_items.items():
            if tool in ["quadrilateral","polygon"]:
                self.draw_temp_items_pos[tool].clear() # 清除临时绘制
                item.setPolygon(QPolygonF())
            elif tool in ["rectangle"]:
                item.setRect(0,0,0,0) # 清除临时绘制
            elif tool in ["arrow"]:
                item.setLine(QLineF())
        self.is_draw_item = False
        self.undo_stack.clear()
        
    def fit_and_center(self, item=None):
        """适应窗口并居中显示图片（简化参数，始终以 image_item 为目标）"""
        if not self.image_item.pixmap().isNull():
            self.fitInView(self.image_item, Qt.KeepAspectRatio)
            self.centerOn(self.image_item)
        # if item and hasattr(self, 'image_item'):
        item = self.image_item
        view_rect = self.viewport().rect()
        # 适应窗口
        # 计算场景中心与视图中心的偏移
        scene_rect = item.sceneBoundingRect().toRect()
        center = view_rect.center() - scene_rect.center()
        self.image_item.moveBy(center.x(), center.y())
        self.fitInView(self.image_item, Qt.KeepAspectRatio)
        # 居中处理
        # self.centerOn(item)
        self.image_item.setZValue(1)
        
    def resizeEvent(self, event: QResizeEvent, /) -> None:
        viewport_rect = self.viewport().rect()
        self.setSceneRect(viewport_rect)
        super().resizeEvent(event)
        
    # def paintEvent(self, event):
    #     super().paintEvent(event)  # 首先调用父类的paintEvent以绘制场景中的内容
    #     # 获取并打印可视区域
    #     viewport_rect = self.viewport().rect()
    #     painter = QPainter(self.viewport())  # 使用viewport()而不是self来绘制视图上的内容
    #     if not painter.isActive():
    #         painter.begin(self)
    #     painter.setPen(QPen(Qt.red, 5))  # 设置画笔为红色，宽度为2
    #     # painter.drawRect(self.image_item.sceneBoundingRect())  # 在可视区域内绘制红框
    #     # painter.drawRect(viewport_rect)  # 在可视区域内绘制红框
    #     painter.setPen(QPen(Qt.GlobalColor.black, 3))  # 设置画笔为红色，宽度为2
    #     painter.drawRect(self.sceneRect())  # 在可视区域内绘制红框
    #     # painter.end()
    
    def add_rect(self, label, value,transliterate=""):
        rect = QRectF(value.get("x"),value.get("y"),value.get("width"),value.get("height"))
        new_item = ResizableGraphicsRectItem(rect)
        color = self.get_shape_attribute_color(label)
        new_item.set_attribute(label=label,color=color,transliterate=transliterate)


        new_item.setParentItem(self.image_item)
        if new_item not in self.scene().items():
            self.scene().addItem(new_item)
        views = self.scene().views()
        if views:
            current_scale = views[0].get_current_scale()
            new_item.set_pen_size(current_scale)
            new_item.update()
        
        
    def add_polygon(self, label, value,transliterate=""):
        all_points_x = value.get("all_points_x")
        all_points_y = value.get("all_points_y")
        points = [QPointF(x,y) for x,y in zip(all_points_x,all_points_y)]
        points = QPolygonF(points)
        new_item = ResizableGraphicsPolygonItem(points)
        color = self.get_shape_attribute_color(label)
        new_item.set_attribute(label=label,color=color,transliterate=transliterate)

        new_item.setParentItem(self.image_item)
        if new_item not in self.scene().items():
            self.scene().addItem(new_item)
        views = self.scene().views()
        if views:
            current_scale = views[0].get_current_scale()
            new_item.set_pen_size(current_scale)
            new_item.update()
                
    def add_quadrilateral(self, label, value,transliterate=""):
        all_points_x = value.get("all_points_x")
        all_points_y = value.get("all_points_y")
        points = [QPointF(x,y) for x,y in zip(all_points_x,all_points_y)]
        points = QPolygonF(points)
        new_item = ResizableGraphicsQuadrilateralItem(points)
        color = self.get_shape_attribute_color(label)
        new_item.set_attribute(label=label,color=color,transliterate=transliterate)
        new_item.setParentItem(self.image_item)
        if new_item not in self.scene().items():
            self.scene().addItem(new_item)
        views = self.scene().views()
        if views:
            current_scale = views[0].get_current_scale()
            new_item.set_pen_size(current_scale)
            new_item.update()
        
            
    def copy_item(self):
        self.copy_items = []
        for item in self.scene().selectedItems():
            if isinstance(item, (ResizableGraphicsRectItem, ResizableGraphicsPolygonItem, ResizableArrowItem)):
                new_item = None
                if isinstance(item, ResizableGraphicsRectItem):
                    new_item = ResizableGraphicsRectItem(item.rect())
                    new_item.setPen(item.pen())
                    # new_item.setBrush(item.brush())
                elif isinstance(item, ResizableGraphicsPolygonItem):
                    new_item = ResizableGraphicsPolygonItem(item.polygon())
                    new_item.setPen(item.pen())
                    # new_item.setBrush(item.brush())
                elif isinstance(item, ResizableArrowItem):
                    new_item = ResizableArrowItem(item.line())
                    new_item.setPen(item.pen())
                if new_item is not None:
                    new_item.setPos(item.pos())
                    new_item.setRotation(item.rotation())
                    new_item.setScale(item.scale())
                    new_item.set_attribute(item.object,item.transliterate,item.pen().color())
                    self.copy_items.append(new_item)
                

    def paste_item(self):
        for item in self.copy_items:
            if isinstance(item, (ResizableGraphicsRectItem, ResizableGraphicsPolygonItem, ResizableArrowItem)):
                new_item = None
                if isinstance(item, ResizableGraphicsRectItem):
                    new_item = ResizableGraphicsRectItem(item.rect())
                    new_item.setPen(item.pen())
                    # new_item.setBrush(item.brush())
                elif isinstance(item, ResizableGraphicsPolygonItem):
                    new_item = ResizableGraphicsPolygonItem(item.polygon())
                    new_item.setPen(item.pen())
                    # new_item.setBrush(item.brush())
                elif isinstance(item, ResizableArrowItem):
                    new_item = ResizableArrowItem(item.line())
                    new_item.setPen(item.pen())
                if new_item is not None:
                    new_item.setPos(item.pos())
                    new_item.setRotation(item.rotation())
                    new_item.setScale(item.scale())
                    new_item.set_attribute(label=item.object)
                    
                    cmd = ItemAddCommand(self.scene(), new_item,self.image_item)
                    self.undo_stack.push(cmd)
                    item.setSelected(True)
        
    def get_current_scale(self):
        """获取当前视图的缩放比例"""
        transform = self.transform()
        # 提取水平和垂直方向的缩放因子
        scale_x = transform.m11()  # 水平缩放因子
        scale_y = transform.m22()  # 垂直缩放因子
        
        # 通常情况下水平和垂直缩放因子相同，取其中一个即可
        return scale_x
    
    def map_mouse_to_image_coordinate(self, event_pos:QPointF) -> QPointF:
        """将鼠标事件坐标映射到图像项的坐标系中"""
        # 视图坐标转场景坐标
        scene_pos = self.mapToScene(event_pos.toPoint())
        scene_pos = self.image_item.mapFromScene(scene_pos) # Scene 坐标转图像项坐标
        return scene_pos
        
    def mousePressEvent(self, event: QMouseEvent):
        """处理鼠标按下事件，分发到不同辅助方法"""
        scene_pos = self.map_mouse_to_image_coordinate(event.position())
        
        if self._handle_control_drag_image(event): # 处理按住Ctrl键拖动图片的逻辑
            super().mousePressEvent(event)
            return
        
        if self._handle_right_click_polygon_finish(event, scene_pos): # 处理右键点击结束多边形绘制的逻辑
            return
            
            
        # if not self.is_draw_item:
        #     super().mousePressEvent(event)
        #     self._clear_selection_if_needed(event, scene_pos)
            
        self._last_left_click_time_pos = [time.time(),scene_pos, True] # [, scene_pos]
        self.click_items = self.get_items_at_click(event.position())
        if QApplication.keyboardModifiers() != Qt.AltModifier and not self.is_draw_item:
            super().mousePressEvent(event)
        # if self._should_start_drawing(event):
        #     self._start_drawing(event, scene_pos)
            
        # if self.is_draw_item:
        #     self.scene().clearSelection()

    def _handle_right_click_polygon_finish(self, event: QMouseEvent, scene_pos: QPointF) -> bool:
        """处理右键点击结束多边形绘制的逻辑"""
        if (event.buttons() & Qt.RightButton 
            and self.current_tool == 'polygon' 
            and self.is_draw_item 
            and len(self.draw_temp_items_pos[self.current_tool]) >= 3):
            
            self._finalize_polygon_drawing()
            return True
        return False

    def _handle_control_drag_image(self, event: QMouseEvent) -> bool:
        """处理按住Ctrl键拖动图片的逻辑"""
        if (QApplication.keyboardModifiers() == Qt.ControlModifier 
            and event.buttons() & Qt.LeftButton):
            # self.image_item.setFlag(QGraphicsItem.ItemIsSelectable, True)
            self.image_item.setFlag(QGraphicsItem.ItemIsMovable, True)
            for item in self.get_image_child_items():
                item.setFlag(QGraphicsItem.ItemIsMovable, False)
                item.setFlag(QGraphicsItem.ItemIsSelectable, False)
            self.scene().clearSelection()
            return True
        self.image_item.setFlag(QGraphicsItem.ItemIsMovable, False)
        for item in self.get_image_child_items():
            item.setFlag(QGraphicsItem.ItemIsMovable, True)
            item.setFlag(QGraphicsItem.ItemIsSelectable, True)
        return False

    def _clear_selection_if_needed(self, event: QMouseEvent, scene_pos: QPointF):
        """在需要时清除选择状态"""
        items = [item for item in self.scene().items(scene_pos.toPoint()) 
                 if not isinstance(item, QGraphicsPixmapItem)]
        if len(items) == 0:
            self.scene().clearSelection()

    def _should_start_drawing(self, event: QMouseEvent) -> bool:
        """判断是否应该开始绘制新图形"""
        selected_items = self.scene().selectedItems()
        items = self.scene().items(event.position().toPoint())
        return (event.buttons() & Qt.LeftButton 
                and (len(selected_items) == 0 or len(items) == 1 
                     or QApplication.keyboardModifiers() == Qt.AltModifier)
                and not self.is_draw_item)

    def _start_drawing(self, event: QMouseEvent, scene_pos: QPointF):
        """根据当前工具类型开始绘制图形"""
        if self.current_tool == 'rectangle':
            self._start_rectangle_drawing(scene_pos)
        elif self.current_tool == 'arrow':
            self._start_arrow_drawing(scene_pos)
        elif self.current_tool in ['quadrilateral', 'polygon']:
            self._start_polygon_drawing(scene_pos)

    def _start_rectangle_drawing(self, scene_pos: QPointF):
        """开始绘制矩形"""
        rectangle = self.draw_temp_items['rectangle']
        self.draw_temp_items_pos['rectangle'] = [scene_pos, scene_pos]
        rectangle.setRect(QRectF(scene_pos, scene_pos).normalized())
        self.is_draw_item = True
        rectangle.setZValue(100)
        rectangle.setVisible(True)
        rectangle.update()

    def _start_arrow_drawing(self, scene_pos: QPointF):
        """开始绘制箭头"""
        arrow = self.draw_temp_items['arrow']
        arrow.setLine(QLineF(scene_pos, scene_pos))
        arrow.setVisible(True)
        arrow.setZValue(100)
        self.is_draw_item = True

    def _start_polygon_drawing(self, scene_pos: QPointF):
        """开始绘制多边形/四边形"""
        self.draw_temp_items_pos[self.current_tool].append(scene_pos)
        polygon = QPolygonF(self.draw_temp_items_pos[self.current_tool])
        polygon_item = self.draw_temp_items[self.current_tool]
        polygon_item.setPolygon(polygon)
        polygon_item.setZValue(100)
        polygon_item.setVisible(True)
        polygon_item.update()
        self.is_draw_item = True

        # 处理四边形自动结束绘制
        if self.current_tool == 'quadrilateral' and len(self.draw_temp_items_pos[self.current_tool]) >= 4:
            self._finalize_quadrilateral_drawing()

    def _finalize_polygon_drawing(self):
        """完成多边形绘制并创建正式图形项"""
        polygon_item = self.draw_temp_items['polygon']
        polygon = QPolygonF(self.draw_temp_items_pos['polygon'])
        results = self.show_property_dialog()
        if results:
        
            new_item = ResizableGraphicsPolygonItem(polygon)
            # new_item.setPen(QPen(results["color"], 2))
            new_item.set_attribute(**results)
            
            cmd = ItemAddCommand(self.scene(), new_item, self.image_item)
            self.undo_stack.push(cmd)
        
        self.draw_temp_items_pos['polygon'].clear()
        self.is_draw_item = False
        polygon_item.setVisible(False)
        polygon_item.setPolygon(QPolygonF())

    def _finalize_quadrilateral_drawing(self):
        """完成四边形绘制并创建正式图形项"""
        polygon_item = self.draw_temp_items['quadrilateral']
        polygon = QPolygonF(self.draw_temp_items_pos['quadrilateral'])
        results = self.show_property_dialog()
        if results:
            new_item = ResizableGraphicsQuadrilateralItem(polygon)
            new_item.set_attribute(**results)
            cmd = ItemAddCommand(self.scene(), new_item, self.image_item)
            self.undo_stack.push(cmd)
        
        self.draw_temp_items_pos['quadrilateral'].clear()
        self.is_draw_item = False
        polygon_item.setVisible(False)
        polygon_item.setPolygon(QPolygonF())

    def get_items_at_click(self,scene_pos):
        items = self.scene().items(scene_pos.toPoint())
        image_child_items = self.get_image_child_items()
        items = [item for item in items if item in image_child_items]
        return items
    
    def mouseMoveEvent(self, event:QMouseEvent):
        scene_pos = self.map_mouse_to_image_coordinate(event.position())
        scaleFactor = self.transform().m11()
        
        ox = self.sceneRect().x()# 相对开始位置x
        oy = self.sceneRect().y() # 相对开始位置y
        if self._handle_control_drag_image(event):
            super().mouseMoveEvent(event)
            return
        if event.buttons() & Qt.LeftButton:
            self._last_left_click_time_pos[2] = True
            if not self.is_click: # 鼠标属于拖动
                selected_items = self.scene().selectedItems()
                click_items = self.click_items
                # 情况1：已有选中项，且未按Alt键（拖动选中项）
                if len(selected_items) > 0 and QApplication.keyboardModifiers() != Qt.AltModifier:
                    # # 关键：确保item始终可移动（在选中时设置一次即可，无需每次移动都设置）
                    # for item in selected_items:
                    #     if not item.flags() & QGraphicsItem.ItemIsMovable:
                    #         item.setFlag(QGraphicsItem.ItemIsMovable, True)
                    # 传递原始事件给父类，让QGraphicsView处理拖动
                    super().mouseMoveEvent(event)
                    return  # 处理完毕，避免后续逻辑干扰
                elif len(click_items) == 0 or (QApplication.keyboardModifiers() == Qt.AltModifier and len(click_items) > 0): # 鼠标没有点击图形项
                    # 鼠标没有点击图形项 或 按下Alt键
                    if self.current_tool == 'rectangle':
                        if not self.is_draw_item:
                            self._start_rectangle_drawing(self._last_left_click_time_pos[1])
                        if self.is_draw_item:
                            # 使用鼠标按下位置和当前位置创建矩形，确保坐标正确
                            self.draw_temp_items_pos[self.current_tool][1]= scene_pos
                            items_pos = self.draw_temp_items_pos[self.current_tool]
                            rect = QRectF(items_pos[0], items_pos[1]).normalized()  # 修复弃用警告
                            RectItem: ResizableGraphicsRectItem = self.draw_temp_items[self.current_tool]
                            RectItem.setRect(rect)
                            RectItem.setZValue(100)
                            RectItem.update()  # 自定义箭头类图元刷新，不然没有双缓冲绘图效果
                    elif self.current_tool == 'arrow':
                        if not self.is_draw_item:
                            self._start_arrow_drawing(scene_pos)
                        if self.is_draw_item:
                            ArrowItem:ResizableArrowItem = self.draw_temp_items[self.current_tool]
                            line = QLineF(ArrowItem.line().p1(), scene_pos)  # 修复弃用警告
                            ArrowItem.setLine(line)
                            ArrowItem.update()  # 自定义箭头类图元刷新，不然没有双缓冲绘图效果
                        
            else: # 鼠标属于点击
                pass
            
        if self.current_tool in ['quadrilateral','polygon']:
            # self.draw_temp_items_pos[self.current_tool].append(scene_pos)
            if len(self.draw_temp_items_pos[self.current_tool]) >= 1:
                polygon = QPolygonF(self.draw_temp_items_pos[self.current_tool] + [scene_pos])
                self.draw_temp_items[self.current_tool].setPolygon(polygon)
        if not self.is_draw_item:
            super().mouseMoveEvent(event)
        
    # ------------------------------------------------------
    # 鼠标释放事件拆分
    # ------------------------------------------------------
    def mouseReleaseEvent(self, event: QMouseEvent):
        """处理鼠标释放事件，分发到不同辅助方法"""
        # if event.button() == Qt.RightButton:
        #     self.pan = False
        #     event.accept()
        #     return

        scene_pos = self.map_mouse_to_image_coordinate(event.position())
        if event.button() & Qt.LeftButton:
            click_items = self.get_items_at_click(event.position())
            is_click = time.time() - self._last_left_click_time_pos[0] < self.click_time_threshold
            if self.is_draw_item or len(click_items) == 0 or (QApplication.keyboardModifiers() == Qt.AltModifier and len(click_items) > 0):
                if is_click and self.current_tool in ['quadrilateral', 'polygon']:
                        self._start_polygon_drawing(scene_pos)
            if self.is_draw_item:
                if self.current_tool == 'rectangle':
                    self._finalize_rectangle_drawing(scene_pos)
                elif self.current_tool == 'arrow':
                    self._finalize_arrow_drawing()
                # if not self.is_draw_item:
                # else:
                #     pass
        self._last_left_click_time_pos = [0,QPointF(),QPointF()]  # 记录上一次点击的坐标和时间
        self._handle_click_blank_area(event, scene_pos)
        super().mouseReleaseEvent(event)

    def _finalize_rectangle_drawing(self, scene_pos: QPointF):
        """完成矩形绘制并创建正式图形项"""
        self.draw_temp_items_pos['rectangle'][1] = scene_pos
        rect = QRectF(*self.draw_temp_items_pos['rectangle']).normalized()
        rectangle = self.draw_temp_items['rectangle']

        if rect.width() > 10 and rect.height() > 10:
            results = self.show_property_dialog()
            if results:
                new_item = ResizableGraphicsRectItem(rect)
                new_item.set_attribute(**results)
                
                cmd = ItemAddCommand(self.scene(), new_item, self.image_item)
                self.undo_stack.push(cmd)
            
        rectangle.setRect(0, 0, 0, 0)
        rectangle.setVisible(False)
        self.is_draw_item = False

    def _finalize_arrow_drawing(self):
        """完成箭头绘制并创建正式图形项"""
        results = self.show_property_dialog()
        if results:
            arrow_item = self.draw_temp_items['arrow']
            line = arrow_item.line()

            new_item = ResizableArrowItem(line)
            new_item.set_attribute(**results)
            
            cmd = ItemAddCommand(self.scene(), new_item, self.image_item)
            self.undo_stack.push(cmd)

        arrow_item.setLine(QLineF())
        arrow_item.setVisible(False)
        self.is_draw_item = False

    def _handle_click_blank_area(self, event: QMouseEvent, scene_pos: QPointF):
        """处理点击空白区域的逻辑（清除选择）"""
        items = self.items(scene_pos.toPoint())
        if len(items) == 1 and isinstance(items[0], QGraphicsPixmapItem):
            self.scene().clearSelection()
        
    def wheelEvent(self, event):
        # if event.modifiers() & Qt.ControlModifier:
        factor = self.scale_factor if event.angleDelta().y() > 0 else 1.0 / self.scale_factor
        # 限制缩放范围
        # new_scale = max(self.min_scale, min(factor, self.max_scale))
        self.apply_scale(factor)
        event.accept()
        # super().wheelEvent(event)
    def apply_scale(self, new_scale):
        """应用缩放比例并调整图形项属性"""
        # 应用视图缩放
        self.scale(new_scale, new_scale)
        current_scale = self.get_current_scale()
        # 调整场景中所有图形项的边框和控制点大小
        for item in self.scene().items():
            if isinstance(item, BaseGraphicsItem):
                # 调整控制点大小
                item.set_pen_size(current_scale)

            
    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Delete or event.key() == Qt.Key_D and event.modifiers() & Qt.ControlModifier: # 删除
            for item in self.scene().selectedItems():
                # self.scene().removeItem(item)
                cmd = ItemDelCommand(self.scene(), item,self.image_item)
                self.undo_stack.push(cmd)
        elif event.key() == Qt.Key_A and event.modifiers() & Qt.ControlModifier: # 全选
            for item in self.scene().items():
                item.setSelected(True)
        elif event.key() == Qt.Key.Key_Escape and self.current_tool in ['quadrilateral','polygon'] and self.is_draw_item: # 全选
            PolygonItem:ResizableGraphicsPolygonItem = self.draw_temp_items[self.current_tool]
            if len(self.draw_temp_items_pos[self.current_tool]) >= 1:
                self.draw_temp_items_pos[self.current_tool].pop(-1)
                polygon = QPolygonF(self.draw_temp_items_pos[self.current_tool])
                PolygonItem.setPolygon(polygon)
                PolygonItem.update()
                if len(self.draw_temp_items_pos[self.current_tool]) == 0:
                    self.is_draw_item = False
            event.ignore()
        elif event.key() == Qt.Key_R and event.modifiers() & Qt.ControlModifier:
            for item in self.scene().selectedItems():
                item.setRotation(item.rotation() + 90)
        elif event.key() == Qt.Key_Plus and event.modifiers() & Qt.ControlModifier:
            for item in self.scene().selectedItems():
                item.setScale(item.scale() * 1.1)
        elif event.key() == Qt.Key_Minus and event.modifiers() & Qt.ControlModifier:
            for item in self.scene().selectedItems():
                item.setScale(item.scale() * 0.9)
        else:
            super(ImageView,self).keyPressEvent(event)
            
    def set_tool(self, tool):
        self.current_tool = tool
        if tool == 'select':
            self.setDragMode(QGraphicsView.RubberBandDrag)
        else:
            self.setDragMode(QGraphicsView.NoDrag)
            
    def show_property_dialog(self,):
        # 设置对话框显示位置（基于鼠标位置）
        global_pos = QCursor.pos()  # 返回QPoint对象
        self.property_dialog.move(global_pos)
        # 显示对话框并处理结果
        if self.property_dialog.exec() == QDialog.Accepted:
            results = self.property_dialog.get_results()
            return results
    
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())